package mya_dc.shared_classes.externalExecuter;

//external free for use class to run command and get result.
//taken from http://www.javalobby.org/java/forums/t53333.html

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
* Usage of following class can go as ...
* <P><PRE><CODE>
* 		SysCommandExecutor cmdExecutor = new SysCommandExecutor();
* 		cmdExecutor.setOutputLogDevice(new LogDevice());
* 		cmdExecutor.setErrorLogDevice(new LogDevice());
* 		int exitStatus = cmdExecutor.runCommand(commandLine);
* </CODE></PRE></P>
* 
* OR
* 
* <P><PRE><CODE>
* 		SysCommandExecutor cmdExecutor = new SysCommandExecutor(); 		
* 		int exitStatus = cmdExecutor.runCommand(commandLine);
* 
* 		String cmdError = cmdExecutor.getCommandError();
* 		String cmdOutput = cmdExecutor.getCommandOutput(); 
* </CODE></PRE></P> 
*/
public class SysCommandExecutor
{	
	private ILogDevice fOuputLogDevice = null;
	private ILogDevice fErrorLogDevice = null;
	private String fWorkingDirectory = null;
	@SuppressWarnings("unchecked")
	private List fEnvironmentVarList = null;
	
	private StringBuffer fCmdOutput = null;
	private StringBuffer fCmdError = null;
	private AsyncStreamReader fCmdOutputThread = null;
	private AsyncStreamReader fCmdErrorThread = null;	
	
	public void setOutputLogDevice(ILogDevice logDevice)
	{
		fOuputLogDevice = logDevice;
	}
	
	public void setErrorLogDevice(ILogDevice logDevice)
	{
		fErrorLogDevice = logDevice;
	}
	
	public void setWorkingDirectory(String workingDirectory) {
		fWorkingDirectory = workingDirectory;
	}
	
	@SuppressWarnings("unchecked")
	public void setEnvironmentVar(String name, String value)
	{
		if( fEnvironmentVarList == null )
			fEnvironmentVarList = new ArrayList();
		
		fEnvironmentVarList.add(new EnvironmentVar(name, value));
	}
	
	public String getCommandOutput() {		
		return fCmdOutput.toString();
	}
	
	public String getCommandError() {
		return fCmdError.toString();
	}
	
	public int runCommand(String commandLine) throws Exception
	{
		/* run command */
		Process process = runCommandHelper(commandLine);
		
		/* start output and error read threads */
		startOutputAndErrorReadThreads(process.getInputStream(), process.getErrorStream());
	    
		/* wait for command execution to terminate */
		int exitStatus = -1;
		try {
			exitStatus = process.waitFor();
					
		} catch (Throwable ex) {
			throw new Exception(ex.getMessage());
			
		} finally {
			/* notify output and error read threads to stop reading */
			//notifyOutputAndErrorReadThreadsToStopReading();
		}
		
		return exitStatus;
	}	
	
	private Process runCommandHelper(String commandLine) throws IOException
	{
		Process process = null;		
		if( fWorkingDirectory == null )
			process = Runtime.getRuntime().exec(commandLine, getEnvTokens());
		else
			process = Runtime.getRuntime().exec(commandLine, getEnvTokens(), new File(fWorkingDirectory));
		
		return process;
	}
	
	private void startOutputAndErrorReadThreads(InputStream processOut, InputStream processErr)
	{
		fCmdOutput = new StringBuffer();
		fCmdOutputThread = new AsyncStreamReader(processOut, fCmdOutput, fOuputLogDevice, "OUTPUT");		
		fCmdOutputThread.start();
		
		fCmdError = new StringBuffer();
		fCmdErrorThread = new AsyncStreamReader(processErr, fCmdError, fErrorLogDevice, "ERROR");
		fCmdErrorThread.start();
	}
	
	@SuppressWarnings("unused")
	private void notifyOutputAndErrorReadThreadsToStopReading()
	{
		fCmdOutputThread.stopReading();
		fCmdErrorThread.stopReading();
	}
	
	@SuppressWarnings("unchecked")
	private String[] getEnvTokens()
	{
		if( fEnvironmentVarList == null )
			return null;
		
		String[] envTokenArray = new String[fEnvironmentVarList.size()];
		Iterator envVarIter = fEnvironmentVarList.iterator();
		int nEnvVarIndex = 0; 
		while (envVarIter.hasNext() == true)
		{
			EnvironmentVar envVar = (EnvironmentVar)(envVarIter.next());
			String envVarToken = envVar.fName + "=" + envVar.fValue;
			envTokenArray[nEnvVarIndex++] = envVarToken;
		}
		
		return envTokenArray;
	}	
}

class AsyncStreamReader extends Thread
{
	private StringBuffer fBuffer = null;
	private InputStream fInputStream = null;
	@SuppressWarnings("unused")
	private String fThreadId = null;
	private boolean fStop = false;
	private ILogDevice fLogDevice = null;
	
	@SuppressWarnings("unused")
	private String fNewLine = null;
	
	public AsyncStreamReader(InputStream inputStream, StringBuffer buffer, ILogDevice logDevice, String threadId)
	{
		fInputStream = inputStream;
		fBuffer = buffer;
		fThreadId = threadId;
		fLogDevice = logDevice;
		
		fNewLine = System.getProperty("line.separator");
	}	
	
	public String getBuffer() {		
		return fBuffer.toString();
	}
	
	public void run()
	{
		try {
			readCommandOutput();
		} catch (Exception ex) {
			//ex.printStackTrace(); //DEBUG
		}
	}
	
	private void readCommandOutput() throws IOException
	{		
		BufferedReader bufOut = new BufferedReader(new InputStreamReader(fInputStream));		
		
		String strLine = null; 
		while ( (fStop == false) && ((strLine = bufOut.readLine()) != null) ) 
		{
			//c = (char)charInt;
			//fBuffer.append(strLine);
			printToDisplayDevice(strLine);
		}		
		bufOut.close();
		//printToConsole("END OF: " + fThreadId); //DEBUG
	}
	
	public void stopReading() {
		fStop = true;
	}
	
	private void printToDisplayDevice(String str)
	{
		if( fLogDevice != null )
			fLogDevice.log(str);
		else
		{
			//printToConsole(c);//DEBUG
		}
	}
	
	@SuppressWarnings("unused")
	private synchronized void printToConsole(char c) {
		System.out.print(c);
	}
}

class EnvironmentVar
{
	public String fName = null;
	public String fValue = null;
	
	public EnvironmentVar(String name, String value)
	{
		fName = name;
		fValue = value;
	}
}


